﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnitsAndBuilduings;
using Microsoft.Xna.Framework;

using Omron.Framework;
using Omron.Framework.Networking;

using Lidgren.Network;
using Lidgren.Network.Xna;

namespace Omron.Actors
{
    public class Factory : FatherBuilding, IFullSynchronize, IPushUpdate
    {//factory unit
        public Factory(BuilduingTypeInfo info, string type, Vector2 loc)
            : base(info, type, loc)
        {
            if ((info.SubTypeCosts.Length != info.SubTypes.Length) || (info.ActionTimes.Length != info.SubTypes.Length))
                throw new Exception("Unit Subtypes must have the same length as SubTypeCosts and as ActionTimes!!");

            spawnTimes = info.ActionTimes;
            elapsedTime = 0;
            units = info.SubTypes;
            costs = new ResourceData[info.SubTypeCosts.Length];
            int ind = 0;
            foreach (ResourceCostInfo[] inf in info.SubTypeCosts)
            {
                costs[ind] = UnitConverter.CreateResourceData(inf);
                ind++;
            }

            string[] menuStrs = new string[info.SubTypes.Length];
            for (int i = 0; i < menuStrs.Length; i++)
            {
                menuStrs[i] = info.SubTypes[i] + "\n" + createResourceDesc(info.SubTypeCosts[i]);
            }
            menu = new ActorMenu(this, menuStrs);
            menu.CommandInvoked += new MenuItemPressed(menu_ItemClicked);
            queue = new Queue<byte>();
        }

        string createResourceDesc(ResourceCostInfo[] infos)
        {
            string str = "";
            foreach (ResourceCostInfo info in infos)
            {
                str += info.Resource + ": " + info.Cost + ", ";
            }
            return str.TrimEnd(new char[2]{' ', ','});
        }

        void menu_ItemClicked(int itemKey)
        {
            Build(itemKey);
        }

        float[] spawnTimes;
        public float elapsedTime;
        string[] units;
        ResourceData[] costs;
        ActorMenu menu;
        public Queue<byte> queue;

        public bool Build(int itemKey)
        {
            if (Faction.Resources >= costs[itemKey])
            {
                Faction.Resources -= costs[itemKey];
                queue.Enqueue((byte)itemKey);
                pushEnqueue((byte)itemKey);
                return true;
            }
            return false;
        }

        public override void WriteOutUpdateData(Lidgren.Network.NetOutgoingMessage om)
        {
            base.WriteOutUpdateData(om);

            om.Write(this.elapsedTime);
        }
        public override void ReadInUpdateData(Lidgren.Network.NetIncomingMessage im)
        {
            base.ReadInUpdateData(im);

            elapsedTime = im.ReadFloat();

            updateMenu();
        }

        enum ActionType : byte
        {
            Enqueue,
            Dequeue
        }
        void pushEnqueue(byte data)
        {
            NetOutgoingMessage update = stage.CreateServerOM();
            if (update != null)
            {
                update.Write((byte)ActionType.Enqueue);
                update.Write(data);
                stage.PushUpdateActor(this, update);
            }
        }
        void pushDequeue()
        {
            NetOutgoingMessage update = stage.CreateServerOM();
            if (update != null)
            {
                update.Write((byte)ActionType.Dequeue);
                stage.PushUpdateActor(this, update);
            }
        }
        public void ReadInPushUpdateData(NetIncomingMessage im)
        {
            switch ((ActionType)im.ReadByte())
            {
                case ActionType.Dequeue:
                    queue.Dequeue();
                    break;
                case ActionType.Enqueue:
                    queue.Enqueue(im.ReadByte());
                    break;
            }
            updateMenu();
        }

        public override ActorMenu Menu
        {
            get 
            { 
                if (IsComplete)
                    return menu;
                return base.Menu;
            }
        }

        void updateMenu()
        {
            menu.DisplayBar = true;
            if (queue.Count > 0)
                menu.BarValue = (float)elapsedTime / spawnTimes[queue.Peek()];
            else
                menu.DisplayBar = false;


            menu.Info = "building cue: ";
            int counter = 3;
            foreach (byte i in queue)
            {
                counter--;
                if (counter == 0)
                {
                    counter = 3;
                    menu.Info += "\n";
                }
                if (i < 0 || i > units.Length) 
                    break;
                menu.Info += units[i] + ", ";
            }
            menu.Info.TrimEnd(new char[] { ' ', ',' });
        }
        public override void UpdateFast(GameTime gameTime)
        {
            base.UpdateFast(gameTime);

            updateMenu();

            if (queue.Count != 0)
            {
                SetBaseAnimation(WorkAnimation);

                elapsedTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
                var top = queue.Peek();
                if (elapsedTime >= spawnTimes[top])
                {
                    elapsedTime -= spawnTimes[top];
                    spawnUnit(units[top]);
                    queue.Dequeue();
                    pushDequeue();
                }
            }
            else if (IsComplete)
            {
                SetBaseAnimation(IdleAnimation);
            }
        }

        void spawnUnit(string unit)
        {

            Actor act = UnitConverter.CreateActor(unit, Position, this.Faction);
            if (act is FatherUnit)
            {
                //set target
                int leastUnits = int.MaxValue;
                Vector2 target = Vector2.Zero;
                foreach (Tile loc in stage.TileGrid.GetRadiusCells(GetPosUV(), 2))//change the radius later
                {
                    if (loc.ProximityActors.Count < leastUnits)
                    {
                        leastUnits = loc.ProximityActors.Count;
                        target = loc.Position;
                    }
                }
                Vector2 actOffset = Vector2.Normalize(target - Position) * this.polygon.MaxRadius;
                (act as FatherUnit).Track(target);
                act.Position += actOffset;
            }
            else//if it isnt a fatherUnit, its position has not been modified
                act.Position += Vector2.One * this.polygon.MaxRadius;
            stage.AddActor(act);
        }

    }
}
